package com.llb666.e.shop.domain.service;

import com.llb666.e.shop.api.vo.MerchantOrdersVo;
import com.llb666.e.shop.api.vo.OrderVo;
import com.llb666.e.shop.domain.entity.*;
import com.llb666.e.shop.domain.entity.bo.ShopCartBo;
import com.llb666.e.shop.domain.entity.bo.SubmitOrderBo;
import com.llb666.e.shop.domain.repository.*;
import com.llb666.e.shop.infracore.enums.OrderStatusEnum;
import lombok.RequiredArgsConstructor;
import org.n3r.idworker.Sid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author llb666
 * @date 2022/9/13 0:34
 */
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class OrderService {

    private final OrdersRepository ordersRepository;
    private final OrderItemsRepository orderItemsRepository;
    private final OrderStatusRepository orderStatusRepository;
    private final ItemsRepository itemsRepository;
    private final ItemsImgRepository itemsImgRepository;
    private final ItemsSpecRepository itemsSpecRepository;
    private final UserAddressRepository userAddressRepository;

    private final Sid sid;

    /**
     * 下单.
     *
     * @param shopCartList  购物车
     * @param submitOrderBo 订单
     * @return 订单信息.
     */
    public OrderVo create(List<ShopCartBo> shopCartList, SubmitOrderBo submitOrderBo) {
        String userId = submitOrderBo.getUserId();
        String addressId = submitOrderBo.getAddressId();
        String itemSpecIds = submitOrderBo.getItemSpecIds();
        Integer payMethod = submitOrderBo.getPayMethod();
        //包邮费用设置为0
        int postAmount = 0;

        // 1. 循环根据itemSpecIds保存订单商品信息表
        String[] itemSpecIdArr = itemSpecIds.split(",");
        // 商品原价累计
        int totalAmount = 0;
        // 优惠后的实际支付价格累计
        int realPayAmount = 0;
        // 结算完成后，等待移除的购物车列表
        List<ShopCartBo> toBeRemovedShopCartList = new ArrayList<>();

        // 优化成批量处理.
        List<String> itemSpecIdList = Arrays.asList(itemSpecIdArr);
        List<String> itemIds = shopCartList.stream().map(ShopCartBo::getItemId).collect(Collectors.toList());

        // 多线程批量查询.
        CompletableFuture<List<ItemsSpec>> itemsSpecsFuture = itemsSpecRepository.getItemSpecFutureByIds(itemSpecIdList);
        CompletableFuture<List<Items>> itemsFuture = itemsRepository.getItemsFutureByIds(itemIds);
        CompletableFuture<List<ItemsImg>> itemsImgFuture = itemsImgRepository.queryItemMainImgsItemsImgByIds(itemIds);
        CompletableFuture<UserAddress> userAddressFuture = userAddressRepository.getFutureByUserIdAndAddressId(userId, addressId);

        CompletableFuture.allOf(itemsSpecsFuture, itemsFuture, itemsImgFuture, userAddressFuture);
        List<ItemsSpec> itemsSpecList = itemsSpecsFuture.join();
        List<Items> itemsList = itemsFuture.join();
        List<ItemsImg> itemsImgList = itemsImgFuture.join();
        UserAddress userAddress = userAddressFuture.join();

        Map<String, ItemsSpec> itemsSpecMap = itemsSpecList.stream().collect(Collectors.toMap(ItemsSpec::getId, itemsSpec -> itemsSpec));
        Map<String, Items> itemsMap = itemsList.stream().collect(Collectors.toMap(Items::getId, items -> items));
        Map<String, ItemsImg> itemsImgMap = itemsImgList.stream()
                .filter(e-> e.getIsMain().equals(1))
                .collect(Collectors.toMap(ItemsImg::getItemId, itemsImg -> itemsImg));

        // 2.构建订单Do TODO 改成只用数字的订单号
        String orderId = sid.nextShort();
        for (String itemSpecId : itemSpecIdList) {
            // 整合redis后，商品购买的数量重新从redis的购物车中获取 fixme 提交订单页面在项目重启后再提交订单会报空指针
            ShopCartBo cartItem = getBuyCountsFromShopCart(shopCartList, itemSpecId);
            assert cartItem != null;
            int buyCounts = cartItem.getBuyCounts();
            toBeRemovedShopCartList.add(cartItem);

            // 2.1 根据规格id，查询规格的具体信息，主要获取价格.
            ItemsSpec itemSpec = itemsSpecMap.get(itemSpecId);
            totalAmount += itemSpec.getPriceNormal() * buyCounts;
            realPayAmount += itemSpec.getPriceDiscount() * buyCounts;

            // 2.2 根据商品id，获得商品信息以及商品图片.
            String itemId = itemSpec.getItemId();
            Items item = itemsMap.get(itemId);
            String imgUrl = itemsImgMap.get(itemId).getUrl();

            // 2.3 循环保存子订单明细到数据库.
            String subOrderId = sid.nextShort();
            OrderItems subOrderItem = new OrderItems();
            subOrderItem.setId(subOrderId);
            subOrderItem.setOrderId(orderId);
            subOrderItem.setItemId(itemId);
            subOrderItem.setItemName(item.getItemName());
            subOrderItem.setItemImg(imgUrl);
            subOrderItem.setBuyCounts(buyCounts);
            subOrderItem.setItemSpecId(itemSpecId);
            subOrderItem.setItemSpecName(itemSpec.getName());
            subOrderItem.setPrice(itemSpec.getPriceDiscount());
            orderItemsRepository.insert(subOrderItem);

            // 2.4 在用户提交订单以后，规格表中需要扣除库存
            /*
             * TODO 异步扣减库存
             * 下单时候扣减库存马上要去数据库修改数据、直接去数据库修改数据是比较消耗性能的、并发量一大会造成性能很差体验很不好、
             * 所以我们可以直接在下单的时候先不去修改数据库中的库存表，先修改缓存中的数据，然后过一段时间再去修改数据库中的数据，
             * 这样就可以大大提高秒杀下单时的并发性能了
             */
            itemsSpecRepository.decreaseItemSpecStock(itemSpecId, buyCounts);
        }
        Orders order = Orders.createDo(orderId, submitOrderBo, userAddress);
        order.setPostAmount(postAmount);
        order.setTotalAmount(totalAmount);
        order.setRealPayAmount(realPayAmount);
        ordersRepository.insert(order);

        // 3. 保存订单状态表
        OrderStatus waitPayOrderStatus = new OrderStatus();
        waitPayOrderStatus.setOrderId(orderId);
        waitPayOrderStatus.setOrderStatus(OrderStatusEnum.WAIT_PAY.type);
        waitPayOrderStatus.setCreatedTime(new Date());
        orderStatusRepository.insert(waitPayOrderStatus);

        // 4. 构建商户订单，用于传给支付中心
        MerchantOrdersVo merchantOrdersVo = new MerchantOrdersVo();
        merchantOrdersVo.setMerchantOrderId(orderId);
        merchantOrdersVo.setMerchantUserId(userId);
        merchantOrdersVo.setAmount(realPayAmount + postAmount);
        merchantOrdersVo.setPayMethod(payMethod);

        // 5. 构建自定义订单vo
        OrderVo orderVo = new OrderVo();
        orderVo.setOrderId(orderId);
        orderVo.setMerchantOrdersVo(merchantOrdersVo);
        orderVo.setToBeRemovedShopCartList(toBeRemovedShopCartList);

        return orderVo;
    }

    /**
     * 内部接口修改订单状态.
     *
     * @param merchantOrderId 订单Id
     * @param status          订单状态
     */
    public void updateOrderStatus(String merchantOrderId, Integer status) {
        orderStatusRepository.updateOrderStatusByType(merchantOrderId, status);
    }

    /**
     * 内部接口获取订单信息.
     *
     * @param orderId 订单Id
     * @return 订单状态
     */
    public OrderStatus queryOrderStatusInfo(String orderId) {
        return orderStatusRepository.queryOrderStatusInfo(orderId);
    }

    /**
     * 获取redis购物车商品数据.
     *
     * @param shopCartList 购物车商品列表
     * @param specId       规格id
     * @return shopCartBo
     */
    private ShopCartBo getBuyCountsFromShopCart(List<ShopCartBo> shopCartList, String specId) {
        for (ShopCartBo shopCartBo : shopCartList) {
            if (specId.equals(shopCartBo.getSpecId())) {
                return shopCartBo;
            }
        }
        return null;
    }
}
